package com.xysoft.framework.web;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.HttpServletBean;

public class DelegatingServletProxy extends HttpServletBean {

	private static final long serialVersionUID = -9116097840000842408L;

	private WebApplicationContext webApplicationContext;

	private String targetBeanName;

	private Servlet delegate;

	private final Object delegateMonitor = new Object();

	public DelegatingServletProxy() {
	}

	public DelegatingServletProxy(Servlet delegate) {
		Assert.notNull(delegate, "delegate Servlet object must not be null");
		this.delegate = delegate;
	}

	public DelegatingServletProxy(String targetBeanName) {
		this(targetBeanName, null);
	}

	public DelegatingServletProxy(String targetBeanName,
			WebApplicationContext wac) {
		Assert.hasText(targetBeanName,
				"target Servlet bean name must not be null or empty");
		this.setTargetBeanName(targetBeanName);
		this.webApplicationContext = wac;
		if (wac != null) {
			this.setEnvironment(wac.getEnvironment());
		}
	}

	public String getTargetBeanName() {
		return targetBeanName;
	}

	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
	}

	@Override
	protected void initServletBean() throws ServletException {
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				if (this.targetBeanName == null) {
					this.targetBeanName = getServletName();
				}

				WebApplicationContext wac = findWebApplicationContext();
				if (wac != null) {
					this.delegate = initDelegate(wac);
				}
			}
		}
	}

	@Override
	public ServletConfig getServletConfig() {
		Servlet delegateToUse = null;
		synchronized (this.delegateMonitor) {
			delegateToUse = this.delegate;
		}
		if (delegateToUse != null) {
			return delegateToUse.getServletConfig();
		}
		return super.getServletConfig();
	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		Servlet delegateToUse = null;
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				WebApplicationContext wac = findWebApplicationContext();
				this.delegate = initDelegate(wac);
			}
			delegateToUse = this.delegate;
		}
		invokeDelegate(delegateToUse, req, resp);
	}

	@Override
	public String getServletInfo() {
		Servlet delegateToUse = null;
		synchronized (this.delegateMonitor) {
			delegateToUse = this.delegate;
		}
		if (delegateToUse != null) {
			return delegateToUse.getServletInfo();
		}
		return super.getServletInfo();
	}

	@Override
	public void destroy() {
		Servlet delegateToUse = null;
		synchronized (this.delegateMonitor) {
			delegateToUse = this.delegate;
		}
		if (delegateToUse != null) {
			destoryDelegate(delegateToUse);
		}
	}

	protected void destoryDelegate(Servlet delegate) {
		delegate.destroy();
	}

	protected WebApplicationContext findWebApplicationContext() {
		if (this.webApplicationContext != null) {
			if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
				if (((ConfigurableApplicationContext) this.webApplicationContext)
						.isActive()) {
					((ConfigurableApplicationContext) this.webApplicationContext)
							.refresh();
				}
			}
			return this.webApplicationContext;
		}
		return WebApplicationContextUtils
				.getWebApplicationContext(getServletContext());
	}

	protected Servlet initDelegate(WebApplicationContext wac)
			throws ServletException {
		Servlet delegate = wac.getBean(getTargetBeanName(), Servlet.class);
		delegate.init(getServletConfig());
		return delegate;
	}

	protected void invokeDelegate(Servlet delegate, ServletRequest request,
			ServletResponse response) throws ServletException, IOException {
		delegate.service(request, response);
	}

}
